(function (angular) {
    'use strict';
    var commonModule = angular.module('fs.common');
    commonModule.factory('dateService', dateService);
    dateService.$inject = ['$filter', 'storageService', 'formatService'];
    function dateService($filter, storageService, formatService) {
        /**
         * function list
         * 1. isHoliday(nowDate,objHdArry)		//input time is holiday (no Saturday and  no Sunday)
         * 2. getEndDate(nowDate, addnum)		//get maturity date from begin date and tenor (no care about if the maturity date is holiday)
         * 3. getEeffectEndDate(nowDate, addnum,objHdArry)	//get maturity date from begin date and tenor (if the maturity date is holiday then it postpones until it isnot holiday)
         * 4. getDateOffset(beginDate,endDate)  //get tenor between begin date and maturity date (nature day)
         * 5. getEeffectDateOffset(beginDate,endDate,objHdArry)  //get tenor between begin date and maturity date (only workday)
         * 6. isSatOrSunDay(nowDate)   //the date is Saturday or Sunday
         */
        /**
         * @initial month array
         * @params
         * @return
         */
        function _Months() {
            this[0] = 'Jan';
            this[1] = 'Feb';
            this[2] = 'Mar';
            this[3] = 'Apr';
            this[4] = 'May';
            this[5] = 'Jun';
            this[6] = 'Jul';
            this[7] = 'Aug';
            this[8] = 'Sep';
            this[9] = 'Oct';
            this[10] = 'Nov';
            this[11] = 'Dec';
        }
        //define month array
        var mons = new _Months();

        /**
         * @input time is holiday (no Saturday and  no Sunday)
         * @params nowDate - date, formatter is :2003-Jul-29
         * @params objHdArry - holiday array, formatter is :objHdArry[0] = "2003-Jan-01",objHdArry[1] = "2003-Jan-07"...
         * @return true or false
         */
        function isHoliday(nowDate, objHdArry) {
            for (var tmp in objHdArry) {
                if (compareDate(objHdArry[tmp], nowDate) == 0) {
                    return true;
                }
            }
            return false;
        }

        /**
         * @ get maturity date from begin date and tenor (no care about if the maturity date is holiday)
         * @param nowDate - date, formatter is :2003-Jul-29 or 2003-01-29
         * @param space - tenor, integer
         * @return String, formatter is :2003-01-29
         */
        function getEndDate(nowDate, space) {
            var curryDate;
            var endDate;

            curryDate = parseDate(nowDate);
            //get maturity date
            space = parseInt(space);
            curryDate.setDate(curryDate.getDate() + space);

            //transform the format
            return formatService.formatDate(curryDate, 'date');
        }

        /**
         * @ get maturity date from begin date and tenor (no care about if the maturity date is holiday)
         * @param nowDate - date, formatter is :2003-Jul-29 or 2003-01-29
         * @param space - tenor, integer
         * @return Date, no format
         */
        function addDays(nowDate, space) {
            var curryDate;
            var endDate;

            curryDate = parseDate(nowDate);
            //get maturity date
            space = parseInt(space);
            curryDate.setDate(curryDate.getDate() + space);

            return curryDate;
        }

        /**
         * @get maturity date from begin date and tenor (if the maturity date is holiday then it postpones until it isnot holiday)
         * @params nowDate - date, formatter is :2003-Jul-29
         * @params addnum - tenor, integer
         * @params objHdArry - date, formatter is :objHdArry[0] = "2003-Jan-01",objHdArry[1] = "2003-Jan-07"...
         * @return: the maturity date that is not holiday, it's formatter is :2003-Jul-29
         */
        //		function getEeffectEndDate(nowDate, addnum,objHdArry){
        //		    var endDate=getEndDate(nowDate, addnum);
        //		    var _addnum=addnum-0;
        //		    while(isHoliday(endDate,objHdArry) || isSatOrSunDay(endDate)){
        //		    	_addnum=_addnum+1;
        //		    	endDate=getEndDate(nowDate, _addnum);
        //		    }
        //		    return endDate;
        //		}

        /**
         * get effective end date
         * @params nowdate - begin date
         * @params addnum - tenor
         * @params objhdarry - holiday array
         * @params weekEnd - weekend array
         * @params direct - forward or backword when the end day is holiday
         */
        function getEeffectEndDate(nowDate, addnum, objHdArry, weekEnd, direct) {
            var endDate = getEndDate(nowDate, addnum);
            var _addnum = addnum - 0;

            //		    while(isHoliday(endDate,objHdArry) || isWeekend(endDate, weekEnd)){
            // objHdArry is date, so need change endDate to date
            while (isHoliday(parse(endDate), objHdArry)) {
                if (direct == 'N') _addnum = _addnum + 1;
                else _addnum = _addnum - 1;
                endDate = getEndDate(nowDate, _addnum);
            }
            return endDate;
        }

        /**
         * @get tenor between begin date and maturity date (nature day)
         * @params beginDate - begin date, formatter is :2003-Jul-29
         * @params endDate - end date, formatter is :2003-Jul-29
         * @return: the number of nature days betwwen beginDate and endDate
         */
        function getDateOffset(beginDate, endDate) {
            var pd1 = parseDate(beginDate);
            var pd2 = parseDate(endDate);
            var MinMilli = 1000 * 60;
            var HrMilli = MinMilli * 60;
            var DyMilli = HrMilli * 24;
            var difference = Date.UTC(pd2.getFullYear(), pd2.getMonth(), pd2.getDate()) - Date.UTC(pd1.getFullYear(), pd1.getMonth(), pd1.getDate());
            return difference / DyMilli;
        }

        /**
         * @get tenor between begin date and maturity date (workday)
         * @params beginDate - begin date, formatter is :2003-Jul-29
         * @params endDate - end date, formatter is :2003-Jul-29
         * @params beginDate - holiday array, formatter is :objHdArry[0] = "2003-Jan-01",objHdArry[1] = "2003-Jan-07"...
         * @return: the number of valid workdays betwwen beginDate and endDate
         */
        function getEeffectDateOffset(beginDate, endDate, objHdArry) {
            var total = getDateOffset(beginDate, endDate);
            var hoilday = 0;
            for (var k = 0; k <= total; k++) {
                if (isHoliday(getEndDate(beginDate, k + 0), objHdArry) || isSatOrSunDay(getEndDate(beginDate, k + 0))) hoilday = hoilday + 1;
            }
            return total - hoilday;
        }

        /**
         * If the date is weekend.
         * @param strDate - String, formater is :"2003-Jul-29" or "2005-10-10"
         * @return boolean
         */
        function isWeekend(strDate) {
            if (!strDate || strDate == '') {
                return false;
            }

            var curryDate = parseDate(strDate);
            if (curryDate == null) {
                return false;
            }
            return curryDate.getDay() == 0 || curryDate.getDay() == 6;
        }

        /**
         *
         * @param strDateStarting String. "2005-10-10"
         * @param strDateEnd String. "2005-10-10"
         * @return int. if value > 0, the starting date is after the end date; if <, the starting date is before the end date. if =, the same date.
         */
        function compareDate(strDateStarting, strDateEnd) {
            var startDate = parse(strDateStarting);
            var endDate = parse(strDateEnd);
            return getDaysBetweenDates(startDate, endDate);
        }

        /**
         *
         * @param strDate String. "2005-10-10"
         * @return Date
         */
        function parseDate(strDate, fmtStr) {
            return parse(strDate, fmtStr);
        }

        /**
         * Get the last day of month which the assigned date is in.
         * @param assignedDate - String. "2005-10-10"
         * @return Date - String. "2005-10-31"
         * @since 2007.5.23
         */
        function getLastDayOfMonth(assignedDate) {
            var dayOfMonth;
            assignedDate = parseDate(assignedDate);
            var month = assignedDate.getMonth() + 1;
            var year = assignedDate.getFullYear();

            // RETURN 31 DAYS
            if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) {
                dayOfMonth = 31;
            }
            // RETURN 30 DAYS
            else if (month == 4 || month == 6 || month == 9 || month == 11) {
                dayOfMonth = 30;
            }
            // RETURN 29 DAYS
            else if (month == 2) {
                if (isLeapYear(year)) {
                    dayOfMonth = 29;
                }
                // RETURN 28 DAYS
                else {
                    dayOfMonth = 28;
                }
            }
            //get maturity date
            assignedDate.setDate(dayOfMonth);
            //transform the format
            return formatService.formatDate(assignedDate, 'date');
        }
        /**
         * CHECK TO SEE IF YEAR IS A LEAP YEAR.
         * @param Year - integer.
         * @return true - leap year. false - not leap year
         * @since 2007.5.23
         */
        function isLeapYear(Year) {
            if ((Year % 4 == 0 && Year % 100 != 0) || Year % 400 == 0) {
                return true;
            } else {
                return false;
            }
        }

        function parse(dateStr, fmtStr) {
            if (!dateStr || angular.isDate(dateStr)) return dateStr;
            return formatService.parseDate(dateStr, fmtStr || 'date');
        }

        /**
         * Validate date String with format string.
         * @param dateStr
         * @param fmtStr
         * @return the value true if the date string is legal;
         *		   a value false if this Date is illegal;
         */
        function validate(dateStr, fmtStr) {
            return parse(dateStr, fmtStr) != null;
        }

        /**
         * Compares two dates for equality.
         * @param date1
         * @param date2
         * @return the value true if the argument Date is equal to this Date;
         */
        function compare(date1, date2) {
            if (!date1 || !date2) return null;
            var time1 = date1.getTime();
            var time2 = date2.getTime();
            if (time1 > time2) {
                return -1;
            } else if (time1 < time2) {
                return 1;
            } else {
                return 0;
            }
        }

        /**
         * calculate number of days between two dates, beginDate (included) to endDate (excluded).
         * @param endDate the end date of the period.
         * @param beginDate the begin date of the period
         * @return number of days from beginDate (included) to endDate (excluded)
         */
        function getDaysBetweenDates(beginDate, endDate) {
            if (!beginDate || !endDate) return null;
            var beginTime = beginDate.getTime();
            var endTime = endDate.getTime();
            var oneDay = 24 * 60 * 60 * 1000;
            return Math.ceil((endTime - beginTime) / oneDay);
        }

        /**
         * Compares two dates for equality.
         * @param date1
         * @param date2
         * @return the value true if the argument Date is equal to this Date;
         */
        function equals(date1, date2) {
            if (!date1 || !date2) return false;
            return date1.getTime() == date2.getTime();
        }

        /**
         * Compares two dates for equality. ignoring time considerations.
         * @param date1
         * @param date2
         * @return the value true if the argument Date is equal to this Date;
         * @see #equals(date1, date2);
         */
        function equalsIgnoreTime(date1, date2) {
            //Clear the date. set hour, minute, second and millisecond to 0.
            this.clearTime = function (targetDate) {
                targetDate.setHours(0);
                targetDate.setMinutes(0);
                targetDate.setSeconds(0);
                targetDate.setMilliseconds(0);
                return targetDate;
            };
            var date1 = this.clearTime(date1);
            var date2 = this.clearTime(date2);
            return equals(date1, date2);
        }

        /**
         * Date Arithmetic function. Adds the specified (signed) amount of time to the given time field.
         * For example:  add(new Date(), 'M', -5)
         *				 add(new Date(), 'y', 3)
         * @param date
         * @param type
         * @param interval
         * @return date;
         */
        function add(date, type, interval) {
            if (!date || typeof date != 'object') return null;
            if (!type || !interval) return date;
            interval = parseInt(interval);
            if (type == 'y') {
                date.setFullYear(date.getFullYear() + interval);
            } else if (type == 'M') {
                date.setMonth(date.getMonth() + interval);
            } else if (type == 'd') {
                date.setDate(date.getDate() + interval);
            } else if (type == 'h') {
                date.setHours(date.getHours() + interval);
            } else if (type == 'm') {
                date.setMinutes(date.getMinutes() + interval);
            } else if (type == 's') {
                date.setSeconds(date.getSeconds() + interval);
            }
            return date;
        }

        return {
            isHoliday: isHoliday,
            getEndDate: getEndDate,
            addDays: addDays,
            getEeffectEndDate: getEeffectEndDate,
            getDateOffset: getDateOffset,
            getEeffectDateOffset: getEeffectDateOffset,
            isWeekend: isWeekend,
            isLeapYear: isLeapYear,
            getLastDayOfMonth: getLastDayOfMonth,
            compareDate: compareDate,
            equalsIgnoreTime: equalsIgnoreTime,
            parseDate: parseDate
        };
    }
})(angular);